package com.xyz.graph.service.impl;

import com.xyz.graph.pojo.gdata.AddVertexParamPojo;
import com.xyz.graph.pojo.gdata.AddVertexPropParamPojo;
import com.xyz.graph.pojo.gdata.BatchAddVertexParamPojo;
import com.xyz.graph.pojo.gdata.edgeParam.AddEdgeParamPojo;
import com.xyz.graph.service.GDataService;
import lombok.extern.slf4j.Slf4j;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.*;

@Slf4j
@Service
public class GDataServiceImpl implements GDataService {

    @Value("${maxBatchNum:100}")
    private int maxBatchNum;

    @Value("${numOfOneBatch:100}")
    private int numOfOneBatch;

    //实体添加,目前只考虑单个用户使用单个固定图连接，后续使用连接池
    @Override
    public String addVertex(GraphTraversalSource g, AddVertexParamPojo addVertexParamPojo) {
        String graphName = addVertexParamPojo.getGraphName();
        String userId = addVertexParamPojo.getUserId();
        String labelName = addVertexParamPojo.getLabelName();
        String entityIdKey = addVertexParamPojo.getEntityIdKey();
        String entityId = addVertexParamPojo.getEntityId();
        Map<String, String> keyValues = addVertexParamPojo.getKeyValues();
        String result = null;

        if (null == keyValues || keyValues.size() == 0) {
            log.error("keyValues参数为空");
            result= "keyValues参数为空";
            return result;
        }
        if(entityIdKey==null){
            return "entityIdKey为null";
        }

        try {

            //此处判断图数据库中是否已存在person实体
            if("personid".equals(entityIdKey)){
                GraphTraversal<Vertex, Vertex> vertex = g.V().has(entityIdKey, entityId);
                if (vertex.hasNext()) {
                    result = "节点 " + entityId + " 已存在，取消新增";
                    return result;
                }
            }

            GraphTraversal<Vertex, Vertex> newVertex = g.addV(labelName).property(entityIdKey, entityId);
            for (String key : keyValues.keySet()) {
                newVertex = newVertex.property(key, keyValues.get(key));
            }
            if (newVertex.hasNext()) {
                newVertex.next();
                result= "----新增节点成功";
            } else {
                log.error("----新增节点失败");
                result= "----新增节点失败";
            }
        } catch (Exception e) {
            log.error("error:"+e);
            result = "执行新增节点时错误";
        } finally {
            //返回图连接

        }
        return result;

    }

    @Override
    public String deleteVertex(GraphTraversalSource g, String graphName, String userId, String entityIdKey, String entityId) {
        String result = null;
        try {
            GraphTraversal<Vertex, Vertex> vertex = g.V().has(entityIdKey, entityId);
            if (!vertex.hasNext()) {
                log.warn("----节点不存在，无法执行删除操作");
                result = "节点 " + entityId + " 不存在，无法删除";
                return result;
            }

            GraphTraversal<Vertex, Vertex> deleteResult = g.V().has(entityIdKey, entityId).drop();
            if (deleteResult.hasNext()) {
                log.error("----节点删除失败，drop操作无效");
                result = "节点 " + entityId + " 未能成功删除，drop操作无效";
            } else {
                log.info("----节点被成功删除");
                result = "节点 " + entityId + " 成功删除";
            }
        } catch (Exception e) {
            log.error("执行删除节点时出错，" + e.getMessage());
            result="执行删除节点时出错，" + e.getMessage();
        } finally {

        }
        return result;
    }

    @Override
    public String addVertexProperty(GraphTraversalSource g, AddVertexPropParamPojo addVertexPropParamPojo) {
        String graphName = addVertexPropParamPojo.getGraphName();
        String userId = addVertexPropParamPojo.getUserId();
        String entityIdKey = addVertexPropParamPojo.getEntityIdKey();
        String entityId = addVertexPropParamPojo.getEntityId();
        Map<String, String> keyValues = addVertexPropParamPojo.getKeyValues();

        String result = null;
        if (null == keyValues || keyValues.size() == 0) {
            return "keyValues参数为空";
        }


        try {
            GraphTraversal<Vertex, Vertex> vertex = g.V().has(entityIdKey, entityId);
            if (!vertex.hasNext()) {
                log.warn("----节点不存在，无法执行添加属性操作");
                result ="节点 " + entityId + " 不存在，无法添加属性";
                return result;
            }

            GraphTraversal<Vertex, Vertex> addPropResult = g.V().has(entityIdKey, entityId);
            for (String key : keyValues.keySet()) {
                addPropResult = addPropResult.property(key, keyValues.get(key));
            }
            if (addPropResult.hasNext()) {
                log.info("----添加属性操作成功");
                result = "为节点 " + entityId + " 添加属性成功";
            } else {
                log.error("----添加属性操作失败， 修改后结果为空");
                result = "为节点 " + entityId + " 添加属性操作成功，结果无效";
            }
        } catch (Exception e) {
            log.error("执行属性添加时出错，" + e.getMessage());
        } finally {

        }

        return result;
    }

    @Override
    public String deleteVertexProperty(GraphTraversalSource g, String graphName, String userId, String entityIdKey, String entityId, String[] propertyKeys) {
        String result = null;
        try {
            GraphTraversal<Vertex, Vertex> vertex = g.V().has(entityIdKey, entityId);
            if (!vertex.hasNext()) {
                log.warn("----节点不存在，无法执行删除属性操作");
                result = "节点 " + entityId + " 不存在，无法删除属性";

                return result;
            }
            List vertexProp = g.V()
                    .has(entityIdKey, entityId)
                    .properties(propertyKeys).toList();
            if (vertexProp.size() == 0) {
                log.warn("----节点属性不存在，无法执行删除属性操作");
                result = "节点属性不存在，无法删除";
                return result;
            }

            GraphTraversal deletePropResult = g.V().has(entityIdKey, entityId).properties(propertyKeys).drop();

            if (deletePropResult.hasNext()) {
                log.error("----删除节点属性操作失败");
                result = "为节点 " + entityId + " 删除属性失败";
            } else {
                log.info("----删除属性操作成功");
                result = "为节点 " + entityId + " 删除属性成功";
            }
        } catch (Exception e) {
            log.error("执行删除属性时出错，" + e.getMessage());
        } finally {

        }
        return result;
    }

    @Override
    public String updateVertexProperty(GraphTraversalSource g, String graphName, String userId, String entityIdKey, String entityId, String propertyKey, String newPropertyValue) {
        String result = null;
        try {
            Boolean isPropExist =g.V().has(entityIdKey, entityId).has(propertyKey).hasNext();
            if (!isPropExist) {
                log.info("----属性不存在，不允许执行更新操作");
                result = "属性不存在，不允许更新";
                return result;
            }
        } catch (Exception e) {
            log.error("执行更新属性时出错，" + e.getMessage());
        } finally {

        }
        return result;
    }

    @Override
    public String batchAddVertex(GraphTraversalSource g, BatchAddVertexParamPojo batchAddVertexParamPojo) {
        String result = null;
        String graphName = batchAddVertexParamPojo.getGraphName();
        String labelName = batchAddVertexParamPojo.getLabelName();
        String entityIdKey = batchAddVertexParamPojo.getEntityIdKey();
        Map<String, Map<String, String>> propKeyValues = batchAddVertexParamPojo.getPropKeyValues();

        //参数判断
        if (null == propKeyValues || propKeyValues.size() == 0) {
            log.warn("----propKeyValues参数为空");
            return "propKeyValues参数为空";
        }
        if (propKeyValues.size() > maxBatchNum) {
            log.warn("----超出了批处理操作可支持的最大量级，最大可支持 " + maxBatchNum
                    + ", 而请求的参数量级为 " + propKeyValues.size());
            return "超出设定的最大批量处理的量级";
        }

        long batchTimes = propKeyValues.size() / numOfOneBatch;  //需要进行批量提交的次数（当有余数时，该值是需要进行批量提交的次数-1）
        GraphTraversal traversal = null;
        Iterator<String> entityIds = propKeyValues.keySet().iterator();
        for (int i = 0; i < batchTimes; i++) {
            for (int j = 0; j < numOfOneBatch; j++) {
                if (traversal == null) {
                    String entityId = entityIds.next();
                    traversal = g.addV(labelName).property(entityIdKey, entityId);
                    for (String propKey : propKeyValues.get(entityId).keySet()) {
                        traversal.property(propKey, propKeyValues.get(entityId).get(propKey));
                    }
                } else {
                    String entityId = entityIds.next();
                    traversal = traversal.addV(labelName).property(entityIdKey, entityId);
                    for (String propKey : propKeyValues.get(entityId).keySet()) {
                        traversal.property(propKey, propKeyValues.get(entityId).get(propKey));
                    }
                }
            }
            //提交traversal
            if (traversal != null && traversal.hasNext()) {
                traversal.next();
                log.info("---- 第 " + i + " 批次添加成功");
            } else {
                log.error("---- 第 " + i + " 批次添加失败，终止点的批量添加操作");

                return "第%s条到第%s条点数据批量添加失败，请检查点数据的正确性."+i * numOfOneBatch+(i + 1) * numOfOneBatch;
            }
        }
        // （propKeyValues.size() / numOfOneBatch）存在余数时
        for (int remainder = 0; remainder < (propKeyValues.size() - numOfOneBatch * batchTimes); remainder++) {
            if (traversal == null) {
                String entityId = entityIds.next();
                traversal = g.addV(labelName).property(entityIdKey, entityId);
                for (String propKey : propKeyValues.get(entityId).keySet()) {
                    traversal.property(propKey, propKeyValues.get(entityId).get(propKey));
                }
            } else {
                String entityId = entityIds.next();
                traversal = traversal.addV(labelName).property(entityIdKey, entityId);
                for (String propKey : propKeyValues.get(entityId).keySet()) {
                    traversal.property(propKey, propKeyValues.get(entityId).get(propKey));
                }
            }
        }
        if ((propKeyValues.size() % numOfOneBatch) != 0) {
            if (traversal != null && traversal.hasNext()) {
                traversal.next();
                log.info("---- 最后批次添加成功");
            } else {
                log.error("---- 最后批次添加失败，终止点的批量添加操作");

                return "第%s条之后的点数据批量添加失败，请检查点数据的正确性."+(propKeyValues.size() - numOfOneBatch * batchTimes);
            }
        }
        result = "节点批量添加成功";

        return result;
    }

    @Override
    public String addEdge(GraphTraversalSource g, AddEdgeParamPojo addEdgeParamPojo) {
        String result = null;

        String graphName = addEdgeParamPojo.getGraphName();
        String userId = addEdgeParamPojo.getUserId();
        String entitySrcIdKey = addEdgeParamPojo.getEntitySrcIdKey();
        String srcIdColName = addEdgeParamPojo.getSrcIdColName();
        String entitySrcId = addEdgeParamPojo.getEntitySrcId();
        String entityDstIdKey = addEdgeParamPojo.getEntityDstIdKey();
        String dstIdColName = addEdgeParamPojo.getDstIdColName();
        String entityDstId = addEdgeParamPojo.getEntityDstId();
        String labelName = addEdgeParamPojo.getLabelName();
        Map<String, String> keyValues = addEdgeParamPojo.getKeyValues();

        try {
            GraphTraversal addEdgeResult = g.V()
                    .has(entityDstIdKey, entityDstId)
                    .as("a")
                    .V()
                    .has(entitySrcIdKey, entitySrcId)
                    .addE(labelName)
                    .to("a");

            addEdgeResult = addEdgeResult.property(srcIdColName, entitySrcId)
                    .property(dstIdColName, entityDstId);
            if (keyValues != null && keyValues.size() != 0) {
                for (String key : keyValues.keySet()) {
                    addEdgeResult = addEdgeResult.property(key, keyValues.get(key));
                }
            }
            if (addEdgeResult.hasNext()) {
//                log.info("----执行新增边操作成功，结果成功");
                result = "执行新增边结果成功";
            } else {
                log.info("----执行新增边操作成功，结果无效");
                result = "执行新增边执行成功，但结果无效";
            }
        } catch (Exception e) {
            log.error("----执行新增边时出错: ", e);
        } finally {
        }
        return result;
    }

    //基于一个点删除边
    @Override
    public String deleteEdge(GraphTraversalSource g, String graphName, String userId, String entityIdKey, String entityId, String direction, String labelName) {
        String result = null;
        try {
            if (!g.V().has(entityIdKey, entityId).hasNext()) {
                log.warn("点不存在，无法删除边");
                return "点不存在，无法删除边";
            }
            GraphTraversal<Vertex, Vertex> vertex = g.V()
                    .has(entityIdKey, entityId);
            Boolean deleteEdgeResult = false;
            GraphTraversal<Vertex, Edge> deleteEdgeResult4Either_in;
            GraphTraversal<Vertex, Edge> deleteEdgeResult4Either_out;
            switch (direction) {
                case "both":
                    deleteEdgeResult = vertex.bothE(labelName).drop().hasNext();
                    break;
                case "in":
                    deleteEdgeResult = vertex.inE(labelName).drop().hasNext();
                    break;
                case "out":
                    deleteEdgeResult = vertex.outE(labelName).drop().hasNext();
                    break;
                case "either":
                    deleteEdgeResult4Either_in = vertex.inE(labelName).drop();
                    deleteEdgeResult4Either_out = vertex.outE(labelName).drop();
                    deleteEdgeResult = deleteEdgeResult4Either_in.hasNext() && deleteEdgeResult4Either_out.hasNext();
                    break;
            }
            if (!deleteEdgeResult) {
                log.info("----执行删除边操作成功，结果成功");
                result = "执行删除边结果成功";
            } else {
                log.info("----执行删除边执行成功,但结果无效");
                result = "执行删除边执行成功但结果无效";
            }
        } catch (Exception e) {
            log.error("----执行删除边时出错: ", e);
        } finally {

        }
        return result;
    }


}
